"""
---------------------------------------------------------------------
- File: middleware.py
- Date: 2025/7/26
- Author: yangzhide
- Email: xxzjyzd@126.com
- Description: 中间件
---------------------------------------------------------------------
"""

import json
import logging
import time
import uuid
from urllib.parse import parse_qs

from starlette.background import BackgroundTask
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response

# 当前用户信息

from app.models.system_manage.logs import Logs

logger = logging.getLogger(__name__)


async def log_request_response(log_data: dict):
    """一个后台任务，用于异步保存日志到数据库"""
    try:
        await Logs.create(**log_data)
    except Exception as e:
        logger.error(f"后台任务保存日志失败: {e}", exc_info=True)


class ApiLogginMiddleware(BaseHTTPMiddleware):
    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        # -- 1. 请求阶段 --
        start_time = time.time()
        request_id = str(uuid.uuid4())

        # 读取请求相关数据
        request_body_bytes = await request.body()
        # 放回请求体，因为只能读取一次
        request._body = request_body_bytes

        # -- 2. 调用下一个中间件或放行
        response = await call_next(request)
        # 响应体是一个一次性的流，我们需要包装它
        response_body = b""
        async for chunk in response.body_iterator:
            response_body += chunk

        # -- 3. 响应阶段
        process_time = int((time.time() - start_time) * 1000)

        # -- 4. 包装日志数据
        # 请求上下文中读取用户信息
        current_user = getattr(request.state, "current_user", None)
        user_no = current_user.user_no if current_user else None
        user_name = current_user.user_name if current_user else None

        # 处理请求体
        content_type = request.headers.get("content-type", "").lower()
        request_body_log_data = None

        if "application/json" in content_type:
            try:
                request_body_log_data = (
                    json.loads(request_body_bytes) if request_body_bytes else {}
                )
            except (json.JSONDecodeError, UnicodeDecodeError):
                request_body_log_data = {"error": "Invalid JSON"}
        elif "application/x-www-form-urlencode" in content_type:
            try:
                # 登录成功直接读取request.state.form_data
                form_data = getattr(request.state, "form_data", None)
                if form_data:
                    request_body_log_data = form_data
                else:
                    # 如果state中没有，说明登录失败，直接解析
                    parsed_form_data = parse_qs(request_body_bytes.decode())
                    encode_from_data = {}
                    for key, value in parsed_form_data.items():
                        if "username" in key.lower():
                            encode_from_data[key] = (
                                value[0] if len(value) == 1 else value
                            )
                    request_body_log_data = encode_from_data
            except Exception:
                request_body_log_data = {"error": "Invalid JSON"}

        log_data = {
            "request_id": request_id,
            "processing_time_ms": process_time,
            "client_ip": request.client.host,
            "user_no": user_no,
            "user_name": user_name,
            "path": request.url.path,
            "method": request.method,
            "request_headers": dict(request.headers),
            "query_params": dict(request.query_params),
            "request_body": request_body_log_data,
            "status_code": response.status_code,
        }

        # 处理响应体
        try:
            log_data["response_body"] = (
                json.loads(response_body) if response_body else {}
            )
        except json.JSONDecodeError:
            log_data["response_body"] = {"error": "Invalid JSON"}

        # -- 5. 后台任务记录日志
        log_task = BackgroundTask(log_request_response, log_data=log_data)

        return Response(
            content=response_body,
            status_code=response.status_code,
            headers=dict(response.headers),
            media_type=response.media_type,
            background=log_task,  # 附加后台任务
        )
